package cn.iocoder.yudao.module.infra.service.base;

import cn.hutool.core.date.DateException;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.iocoder.yudao.framework.common.exception.BusinessException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
import cn.iocoder.yudao.framework.mybatis.core.mapper.CommonMapper;
import cn.iocoder.yudao.module.infra.api.base.dto.ColumnModel;
import cn.iocoder.yudao.module.infra.api.base.dto.FormNameValueModel;
import cn.iocoder.yudao.module.infra.dal.dataobject.base.AmisDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.base.DynamicTable;
import cn.iocoder.yudao.module.infra.dal.mysql.base.DynamicTableMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class DynamicTableServiceImpl extends ServiceImpl<DynamicTableMapper, DynamicTable> implements DynamicTableService {

    private static final Logger LOG = LoggerFactory.getLogger(DynamicTableServiceImpl.class);

    @Resource
    private DynamicTableMapper dynamicTableMapper;

    @Resource
    private AmisService amisService;

    @Resource
    private CommonMapper commonMapper;

    @Override
    public DynamicTable getDateModelBySqlName(String dataSqlName) {
        LambdaQueryWrapper<DynamicTable> dynamicTableLambdaQueryWrapper = new LambdaQueryWrapper<>();
        return dynamicTableMapper.selectOne(dynamicTableLambdaQueryWrapper.eq(DynamicTable::getDataSqlName, dataSqlName));
    }

    @Override
    public void saveNewDataModel(DynamicTable model) {
        DynamicTable oldModel = this.getDateModelBySqlName(model.getDataSqlName());
        if (oldModel != null) {
            model.setId(oldModel.getId());
            dynamicTableMapper.updateById(model);
            return;
        }
        model.setId(null);
        dynamicTableMapper.insert(model);
    }


    @Override
    public boolean addModelData(String newModelData, String dataSqlName, String opId) throws BusinessException {
        DynamicTable model = this.getDateModelBySqlName(dataSqlName);
        if (model == null) {
            throw new BusinessException("该数据类型不存在");
        }
        if (StringUtils.isBlank(newModelData)) {
            throw new BusinessException("新增数据不能为空");
        }
        List<ColumnModel> cols = JsonUtils.parseArray(model.getColsJson(), ColumnModel.class);
        Map<String, String> colDataMap = JsonUtils.parseObject(newModelData, new TypeReference<Map<String, String>>() {
        });
        List<FormNameValueModel> colDatas = new ArrayList<>();
        for (Map.Entry<String, String> colEnt : colDataMap.entrySet()) {
            FormNameValueModel formNameValueModel = new FormNameValueModel();
            formNameValueModel.setName(colEnt.getKey());
            formNameValueModel.setValue(colEnt.getValue());
            colDatas.add(formNameValueModel);
        }
        StringBuilder sb1 = new StringBuilder();
        sb1.append("insert into ").append(dataSqlName).append("(");

        StringBuilder sb2 = new StringBuilder();
        sb2.append(" values(");

        List<Object> pars = new ArrayList<>();

        if (cols.stream().anyMatch(col -> "creator".equalsIgnoreCase(col.getColSqlName())) && StringUtils.isNotBlank(opId)) {
            sb1.append("creator").append(",");
            sb2.append(" ? ,");
            pars.add(opId);
        }

        for (FormNameValueModel colData : colDatas) {
            if (StringUtils.isBlank(colData.getName()) || StringUtils.isBlank(colData.getValue()) || "id".equalsIgnoreCase(colData.getName()) || "creator,create_time,updater,update_time".contains(colData.getName())) {
                continue;
            }
            for (ColumnModel col : cols) {
                if (col.getColSqlName().equalsIgnoreCase(colData.getName())) {
                    if ("bol".equalsIgnoreCase(col.getColDataType())) {
                        if ("1".equalsIgnoreCase(colData.getValue())) {
                            pars.add(true);
                        } else {
                            pars.add(false);
                        }
                    } else if ("date".equalsIgnoreCase(col.getColDataType())) {
                        try {
                            Date date = DateUtil.parse(colData.getValue());
                            pars.add(date);
                        } catch (DateException e) {
                            LOG.error("时间转换错误", e);
                            throw new BusinessException("数据类型转换错误," + col.getColName() + ":" + colData.getValue());
                        }
                    } else if ("num".equalsIgnoreCase(col.getColDataType())) {
                        try {
                            Integer integer = Integer.valueOf(colData.getValue());
                            pars.add(integer);
                        } catch (NumberFormatException e) {
                            LOG.error("数字转换错误", e);
                            throw new BusinessException("数据类型转换错误," + col.getColName() + ":" + colData.getValue());
                        }

                    } else {
                        pars.add(colData.getValue());
                    }
                    sb1.append("`").append(col.getColSqlName()).append("`").append(",");
                    sb2.append(" ? ,");
                    break;
                }
            }

        }
        sb1.deleteCharAt(sb1.length() - 1).append(")");
        sb2.deleteCharAt(sb2.length() - 1).append(")");
        String insertSql = sb1.append(sb2).toString();
        LOG.info("inset {} data sql is :{}", dataSqlName, insertSql);
        commonMapper.execute(insertSql, pars);
        return true;
    }

    @Override
    public boolean editModelData(String newModelData, String dataSqlName, String opId) throws BusinessException {
        DynamicTable model = this.getDateModelBySqlName(dataSqlName);
        if (model == null) {
            throw new BusinessException("该数据类型不存在");
        }
        if (StringUtils.isBlank(newModelData)) {
            throw new BusinessException("新增数据不能为空");
        }
        List<ColumnModel> cols = JsonUtils.parseArray(model.getColsJson(), ColumnModel.class);
        //Map<String, String> colDataMap = JsonUtils.parseMap(newModelData);
        Map<String, String> colDataMap = JsonUtils.parseObject(newModelData, new TypeReference<Map<String, String>>() {
        });
        List<FormNameValueModel> colDatas = new ArrayList<>();
        for (Map.Entry<String, String> colEnt : colDataMap.entrySet()) {
            FormNameValueModel formNameValueModel = new FormNameValueModel();
            formNameValueModel.setName(colEnt.getKey());
            formNameValueModel.setValue(colEnt.getValue());
            colDatas.add(formNameValueModel);
        }
        StringBuilder sb1 = new StringBuilder();
        sb1.append("update ").append(dataSqlName).append(" set ");
        List<Object> pars = new ArrayList<>();

        if (cols.stream().anyMatch(col -> "updater".equalsIgnoreCase(col.getColSqlName())) && StringUtils.isNotBlank(opId)) {
            sb1.append("updater").append("= ? ,");
            pars.add(opId);
        }

        Long id = null;
        for (FormNameValueModel colData : colDatas) {
            if (StringUtils.isBlank(colData.getName()) || StringUtils.isBlank(colData.getValue()) || "creator,create_time,updater,update_time".contains(colData.getName())) {
                continue;
            }
            if ("id".equalsIgnoreCase(colData.getName())) {
                id = Long.valueOf(colData.getValue());
                continue;
            }
            for (ColumnModel col : cols) {
                if (col.getColSqlName().equalsIgnoreCase(colData.getName()) && "1".equalsIgnoreCase(col.getColCanUpdate())) {
                    if ("bol".equalsIgnoreCase(col.getColDataType())) {
                        if ("1".equalsIgnoreCase(colData.getValue()) || "true".equalsIgnoreCase(colData.getValue())) {
                            pars.add(true);
                        } else {
                            pars.add(false);
                        }
                    } else if ("date".equalsIgnoreCase(col.getColDataType())) {
                        try {
                            Date date = DateUtil.parse(colData.getValue());
                            pars.add(date);
                        } catch (DateException e) {
                            LOG.error("时间转换错误", e);
                            throw new BusinessException("数据类型转换错误," + col.getColName() + ":" + colData.getValue());
                        }
                    } else if ("num".equalsIgnoreCase(col.getColDataType())) {
                        try {
                            Integer integer = Integer.valueOf(colData.getValue());
                            pars.add(integer);
                        } catch (NumberFormatException e) {
                            LOG.error("数字转换错误", e);
                            throw new BusinessException("数据类型转换错误," + col.getColName() + ":" + colData.getValue());
                        }

                    } else {
                        pars.add(colData.getValue());
                    }
                    sb1.append("`").append(col.getColSqlName()).append("`").append("= ? ,");
                    break;

                }
            }

        }
        if (null == id) {
            throw new BusinessException("更新数据主键不能为空");
        }
        sb1.deleteCharAt(sb1.length() - 1).append(" where id = ? ");
        pars.add(id);
        String insertSql = sb1.toString();
        LOG.info("update {} data sql is :{}", dataSqlName, insertSql);
        commonMapper.execute(insertSql, pars);
        return true;
    }


    @Override
    public int deleteModelData(List<Integer> ids, String dataSqlName, String opId) {
        if (StringUtils.isBlank(dataSqlName) || ids == null || ids.size() == 0) {
            return 0;
        }
        StringBuilder delSql = new StringBuilder();
        delSql.append("delete from ").append(dataSqlName).append(" where id in(");
        for (Integer id : ids) {
            if (id != null) {
                delSql.append(id).append(",");
            }
        }
        delSql.deleteCharAt(delSql.length() - 1);
        delSql.append(")");
        LOG.info("delSql is {}", delSql);
        return commonMapper.execute(delSql.toString(), new ArrayList<>());
    }


    @Override
    public PageResult<Map<String, Object>> getDataCols(DynamicTable model, String queryData, Integer pageNum, Integer pageSize) {
        if (model == null) {
            throw new BusinessException("该数据类型不存在");
        }
        if (null == pageNum || pageNum <= 0) {
            pageNum = 1;
        }
        if (pageSize == null || pageSize <= 0) {
            pageSize = 10;
        }
        List<ColumnModel> cols = JsonUtils.parseArray(model.getColsJson(), ColumnModel.class);
        List<String> colNames = new ArrayList<>();
        for (ColumnModel col : cols) {
            colNames.add(col.getColSqlName());
        }
        StringBuilder sb = new StringBuilder();
        String querCol = cols.stream().map(p -> "`" + p.getColSqlName() + "`").collect(Collectors.joining(","));
        sb.append("select ").append(querCol).append(" from ").append(model.getDataSqlName()).append(" where 1=1 ");
        List<Object> pars = new ArrayList<>();
        if (StringUtils.isNoneBlank(queryData)) {
            List<FormNameValueModel> conditionModels = JsonUtils.parseArray(queryData, FormNameValueModel.class);
            for (FormNameValueModel conditionModel : conditionModels) {
                if (StringUtils.isBlank(conditionModel.getName()) || StringUtils.isBlank(conditionModel.getValue())) {
                    continue;
                }
                String colSqlName = conditionModel.getName();
                int queryType = 0;
                if (colSqlName.endsWith("_MIN")) {
                    queryType = -1;
                } else if (colSqlName.endsWith("_MAX")) {
                    queryType = 1;
                }
                if (queryType != 0) {
                    colSqlName = colSqlName.substring(0, colSqlName.length() - 4);
                }
                for (ColumnModel col : cols) {
                    if (colSqlName.equalsIgnoreCase(col.getColSqlName())) {
                        if ("date".equalsIgnoreCase(col.getColDataType())) {
                            try {
                                Date date = DateUtil.parse(conditionModel.getValue());
                                pars.add(date);
                            } catch (DateException e) {
                                LOG.error("时间转换错误", e);
                                throw new BusinessException("数据类型转换错误," + col.getColName() + ":" + conditionModel.getValue());
                            }

                        } else if ("num".equalsIgnoreCase(col.getColDataType())) {
                            pars.add(Long.valueOf(conditionModel.getValue()));
                        } else {
                            if ("2".equalsIgnoreCase(col.getColQueryType())) {
                                pars.add("%" + conditionModel.getValue() + "%");
                            } else {
                                pars.add(conditionModel.getValue());
                            }
                        }
                        if (queryType == 0) {
                            if ("1".equalsIgnoreCase(col.getColQueryType())) {
                                sb.append(" and ").append(colSqlName).append(" =? ");
                            } else if ("2".equalsIgnoreCase(col.getColQueryType())) {
                                sb.append(" and ").append(colSqlName).append(" like ? ");
                            }
                        } else if (queryType == -1) {
                            sb.append(" and ").append(colSqlName).append(" >=? ");
                        } else {
                            sb.append(" and ").append(colSqlName).append(" <=? ");
                        }
                        break;
                    }
                }

            }
        }

        String selectSql = sb.deleteCharAt(sb.length() - 1).append(" order by ").append(cols.get(0).getColSqlName()).append(" desc ").toString();

        List<Map<String, Object>> maps = commonMapper.listMapsByPage(selectSql, colNames, pageNum, pageSize, pars);
        Long total = commonMapper.getTotalCount(selectSql.replace(querCol, "count(1)"), pars);
        return new PageResult<>(maps, total);
    }


    @Override
    public PageResult<Map<String, Object>> listXCRUDData(String dataName, JSONObject params) {
        AmisDO amisData = amisService.getByType(dataName);
        ValidationUtils.validNotBeNull(amisData, "amisData");
        JSONObject yuanData = JsonUtils.parseObject(amisData.getJsonData(), JSONObject.class);
        ValidationUtils.validNotBeNull(yuanData, "yuanData");
        String primaryKeyCol = StrUtil.toSymbolCase(yuanData.getStr("primaryKey"), '_');
        StringBuilder querCol = new StringBuilder();
        querCol.append(primaryKeyCol).append(",");
        List<String> colNames = new ArrayList<>();
        colNames.add(primaryKeyCol);
        JSONArray columns = yuanData.getJSONArray("columns");
        for (int i = 0; i < columns.size(); i++) {
            String colName = StrUtil.toSymbolCase(columns.getJSONObject(i).getStr("field"), '_');
            querCol.append(colName).append(",");
            colNames.add(colName);
        }
        querCol.deleteCharAt(querCol.length() - 1);
        StringBuilder sb = new StringBuilder("select ").append(querCol).append(" from ").append(dataName);
        sb.append(" where 1=1");
        int pageNum = 1;
        int pageSize = 10;
        Set<Map.Entry<String, Object>> entries = params.entrySet();
        List<Object> pars = new ArrayList<>();
        for (Map.Entry<String, Object> i : entries) {
            if ("pageSize".equals(i.getKey())) {
                pageSize = (int) i.getValue();
            } else if ("pageNo".equals(i.getKey())) {
                pageNum = (int) i.getValue();
            } else if (i.getValue() != null && StrUtil.isNotBlank((CharSequence) i.getValue())) {
                sb.append(" and ").append(StrUtil.toSymbolCase(i.getKey(), '_')).append(" = ? ");
                pars.add(i.getValue());
            }
        }
        List<Map<String, Object>> maps = commonMapper.listMapsByPage(sb.toString(), colNames, pageNum, pageSize, pars);
        Long total = commonMapper.getTotalCount(sb.toString().replace(querCol.toString(), "count(1)"), pars);
        return new PageResult<>(maps, total);
    }

    @Override
    public void addXCRUDData(String dataName, JSONObject params, String opId) {

    }

    @Override
    public void updateXCRUDData(String dataName, JSONObject params, String opId) {

        AmisDO amisData = amisService.getByType(dataName);
        ValidationUtils.validNotBeNull(amisData, "amisData");
        JSONObject yuanData = JsonUtils.parseObject(amisData.getJsonData(), JSONObject.class);
        ValidationUtils.validNotBeNull(yuanData, "yuanData");
        String primaryKeyCol = StrUtil.toSymbolCase(yuanData.getStr("primaryKey"), '_');

        StringBuilder sb = new StringBuilder();
        sb.append("update ").append(dataName).append(" set ");
        Set<Map.Entry<String, Object>> entries = params.entrySet();
        List<Object> pars = new ArrayList<>();
        for (Map.Entry<String, Object> i : entries) {
            if (!primaryKeyCol.equalsIgnoreCase(i.getKey())) {
                sb.append(StrUtil.toSymbolCase(i.getKey(), '_')).append(" = ? ,");
                pars.add(i.getValue());
            }
        }
        sb.deleteCharAt(sb.length() - 1).append(" where id = ? ");
        pars.add(params.getLong(primaryKeyCol));

        commonMapper.execute("delete " + " from " + dataName + " where " + primaryKeyCol + " = ?", pars);
    }

    @Override
    public void delXCRUDData(String dataName, long id, String opId) {
        AmisDO amisData = amisService.getByType(dataName);
        ValidationUtils.validNotBeNull(amisData, "amisData");
        JSONObject yuanData = JsonUtils.parseObject(amisData.getJsonData(), JSONObject.class);
        ValidationUtils.validNotBeNull(yuanData, "yuanData");
        String primaryKeyCol = StrUtil.toSymbolCase(yuanData.getStr("primaryKey"), '_');

        List<Object> pars = new ArrayList<>();
        pars.add(id);

        commonMapper.execute("delete " + " from " + dataName + " where " + primaryKeyCol + " = ?", pars);
    }

    public Map<String, Object> getXCRUDData(String dataName, long id, String opId) {

        AmisDO amisData = amisService.getByType(dataName);
        ValidationUtils.validNotBeNull(amisData, "amisData");
        JSONObject yuanData = JsonUtils.parseObject(amisData.getJsonData(), JSONObject.class);
        ValidationUtils.validNotBeNull(yuanData, "yuanData");
        String primaryKeyCol = StrUtil.toSymbolCase(yuanData.getStr("primaryKey"), '_');

        StringBuilder querCol = new StringBuilder();
        querCol.append(primaryKeyCol).append(",");
        List<String> colNames = new ArrayList<>();
        colNames.add(primaryKeyCol);
        JSONArray columns = yuanData.getJSONArray("columns");
        for (int i = 0; i < columns.size(); i++) {
            String colName = StrUtil.toSymbolCase(columns.getJSONObject(i).getStr("field"), '_');
            querCol.append(colName).append(",");
            colNames.add(colName);
        }
        querCol.deleteCharAt(querCol.length() - 1);
        List<Object> pars = new ArrayList<>();
        pars.add(id);

        List<Map<String, Object>> maps = commonMapper.listMaps("select " + querCol + " from " + dataName + " where " + primaryKeyCol + " = ?", colNames, pars);
        if (!CollectionUtils.isAnyEmpty(maps)) {
            return maps.get(0);
        }
        return null;
    }
}
